package service // 业务逻辑服务层，处理核心业务逻辑

import (
	"encoding/json"
	"errors"             // 错误处理包，用于创建和比较错误
	"gin_gorm_oj/define" // 导入项目定义常量包
	"gin_gorm_oj/helper"
	"gin_gorm_oj/models" // 导入数据模型包
	"gorm.io/gorm"       // GORM ORM框架
	"log"                // 导入日志包
	"net/http"           // 导入HTTP包
	"strconv"            // 导入字符串转换包

	"github.com/gin-gonic/gin" // 导入gin框架
)

// GetProblemList
// @Tags      公共方法
// @Summary   问题列表
// @Description 获取问题列表，支持分页和关键词搜索
// @Accept    json
// @Produce   json
// @Param     page      query    int     false  "页码"
// @Param     size      query    int     false  "每页大小"
// @Param     keyword   query    string  false  "关键词"
// @Param 	  category_id query string false "category_id"
// @Success   200       {string} json    {"code":"200","msg":"","data":""}
// @Router    /problem-list [get]
func GetProblemList(c *gin.Context) {
	// 获取每页大小参数，默认使用定义值
	size, _ := strconv.Atoi(c.DefaultQuery("size", define.DefaultSize)) // 解析size参数，默认使用配置的页大小
	// 获取页码参数，默认使用定义值
	page, err := strconv.Atoi(c.DefaultQuery("page", define.DefaultPage)) // 解析page参数，默认使用配置的页码
	// 检查页码参数转换错误
	if err != nil { // 如果页码参数格式错误
		log.Println("GetProblemList Page strconv Error:", err) // 记录转换错误日志
		return                                                 // 直接返回，终止处理
	}
	page = (page - 1) * size             // 计算数据库查询偏移量，转换为从0开始的索引
	keyword := c.Query("keyword")        // 获取搜索关键词参数
	categoryId := c.Query("category_id") // 获取分类ID参数

	var count int64            // 声明总记录数变量，用于存储符合条件的总记录数
	var data []*models.Problem // 声明存储查询结果的数据切片
	// 调用模型层获取查询构建器
	tx := models.GetProblemList(keyword, categoryId) // 使用模型层的方法获取预处理的查询构建器
	// 执行分页查询，排除content字段
	err = tx.Count(&count).Omit("content").Offset(page).Limit(size).Find(&data).Error // 链式调用执行查询，获取总数和分页数据
	if err != nil {                                                                   // 检查查询错误
		log.Println("Get Problem List Error:", err) // 记录查询错误日志
		return                                      // 直接返回，终止处理
	}

	c.JSON(http.StatusOK, gin.H{ // 返回HTTP 200状态和JSON响应
		"code": 200, // 响应状态码
		"data": map[string]interface{}{ // 响应数据部分
			"data":  data,  // 问题列表数据
			"count": count, // 总记录数
		},
	})
}

// GetProblemDetail
// @Tags 公共方法
// @Summary 问题详情
// @Param identity query string false "problem identity"
// @Success 200 {string} json "{"code":"200","data":"{}"}"
// @Router /problem-detail [get]
func GetProblemDetail(c *gin.Context) {
	identity := c.Query("identity") // 获取问题唯一标识参数
	if identity == "" {             // 检查标识是否为空
		c.JSON(http.StatusOK, gin.H{"code": -1, "msg": "问题唯一标识不能为空"})
		return // 终止处理
	}
	var data models.Problem // 声明问题数据模型变量
	// 查询问题详情，预加载关联的分类信息
	err := models.DB.Preload("ProblemCategories.Category").
		Where("identity = ?", identity).First(&data).Error

	if err != nil { // 检查查询错误
		if errors.Is(err, gorm.ErrRecordNotFound) { // 判断是否为记录未找到错误
			c.JSON(http.StatusOK, gin.H{"code": -1, "msg": "问题不存在"})
			return // 终止处理
		}
		c.JSON(http.StatusOK, gin.H{"code": -1, "msg": "Get ProblemDetail Error:" + err.Error()})
		return // 终止处理
	}
	// 返回成功响应
	c.JSON(http.StatusOK, gin.H{"code": 200, "data": data})
}

// ProblemCreate 问题创建接口
// @Tags 管理员私有方法
// @Summary 问题创建
// @Param token header string true "token"
// @Param title formData string true "title"
// @Param content formData string true "content"
// @Param max_runtime formData int false "max_runtime"
// @Param max_mem formData int false "max_mem"
// @Param category_ids formData array false "category_ids"
// @Param test_cases formData array true "test_cases"
// @Success 200 {string} json {"code":"200","data:""}"
// @Router /admin/problem-create [post]
func ProblemCreate(c *gin.Context) {
	// 获取表单参数
	title := c.PostForm("title")                             // 问题标题
	content := c.PostForm("content")                         // 问题内容
	maxRuntime, _ := strconv.Atoi(c.PostForm("max_runtime")) // 最大运行时间（转换为整数）
	maxMemory, _ := strconv.Atoi(c.PostForm("max_mem"))      // 最大内存（转换为整数）
	categoryIds := c.PostFormArray("category_ids")           // 分类ID数组
	testCases := c.PostFormArray("test_cases")               // 测试用例数组

	// 参数校验
	if title == "" || content == "" || len(categoryIds) == 0 || len(testCases) == 0 {
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "参数不能为空", // 返回错误信息
		})
		return // 终止执行
	}

	identity := helper.GetUUID()
	// 创建问题数据结构体
	data := &models.Problem{
		Identity:   identity,
		Title:      title,      // 问题标题
		Content:    content,    // 问题内容
		MaxRuntime: maxRuntime, // 最大运行时间限制
		MaxMem:     maxMemory,  // 最大内存限制
	}

	// 先将问题数据插入数据库，获取自增ID
	err := models.DB.Create(data).Error
	if err != nil {
		// 插入失败，返回错误信息
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "Problem Create Error:" + err.Error(), // 拼接错误详情
		})
		return // 终止执行
	}

	// 处理分类
	// 创建分类关系切片
	category := make([]*models.ProblemCategory, 0)

	// 遍历分类ID数组
	for _, id := range categoryIds {
		categoryId, _ := strconv.Atoi(id) // 字符串转换为整数

		// 创建问题-分类关联对象并添加到切片（现在data.ID已获取）
		category = append(category, &models.ProblemCategory{
			ProblemId:  data.ID,
			CategoryId: uint(categoryId),
		})
	}

	// 批量创建问题分类关联
	if len(category) > 0 {
		err = models.DB.Create(&category).Error
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "Problem Category Create Error:" + err.Error(),
			})
			return
		}
	}

	// 处理测试用例
	testCaseBasics := make([]*models.TestCase, 0)
	// 遍历测试用例数组
	for _, testCase := range testCases {
		// 举个例子：{"input":"1 2\n","output":"3\n"}
		// 创建map用于存储解析后的测试用例数据
		caseMap := make(map[string]string)

		// 将JSON字符串解析到map中
		err := json.Unmarshal([]byte(testCase), &caseMap)
		if err != nil {
			// JSON解析失败，返回格式错误
			c.JSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "测试用例格式错误",
			})
			return
		}

		// 检查是否包含input字段
		if _, ok := caseMap["input"]; !ok {
			// 缺少input字段，返回格式错误
			c.JSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "测试用例格式错误",
			})
			return
		}

		// 检查测试用例中是否包含output字段
		if _, ok := caseMap["output"]; !ok {
			// 如果缺少output字段，返回错误响应
			c.JSON(http.StatusOK, gin.H{
				"code": -1,         // 错误状态码
				"msg":  "测试用例格式错误", // 错误提示信息
			})
			return // 终止函数执行
		}

		// 创建测试用例数据结构
		testCaseBasic := &models.TestCase{
			Identity:        helper.GetUUID(),  // 生成唯一标识
			ProblemIdentity: identity,          // 关联问题ID
			Input:           caseMap["input"],  // 测试输入数据
			Output:          caseMap["output"], // 期望输出结果
		}
		testCaseBasics = append(testCaseBasics, testCaseBasic)
	}

	// 批量创建测试用例
	if len(testCaseBasics) > 0 {
		err = models.DB.Create(&testCaseBasics).Error
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": -1,
				"msg":  "Test Case Create Error:" + err.Error(),
			})
			return
		}
	}

}

// ProblemModify 问题修改接口
// @Tags 管理员私有方法
// @Summary 问题修改
// @Param authorization header string true "authorization"
// @Param identity formData string true "identity"
// @Param title formData string true "title"
// @Param content formData string true "content"
// @Param max_runtime formData int true "max_runtime"
// @Param max_mem formData int true "max_mem"
// @Param category_ids formData []string false "category_ids" collectionFormat(multi)
// @Param test_cases formData []string true "test_cases" collectionFormat(multi)
// @Success 200 {string} json {"code":"200","data:""}
// @Router /admin/problem-modify [put]
func ProblemModify(c *gin.Context) {
	// 获取表单参数
	identity := c.PostForm("identity")                       // 问题标识
	title := c.PostForm("title")                             // 问题标题
	content := c.PostForm("content")                         // 问题内容
	maxRuntime, _ := strconv.Atoi(c.PostForm("max_runtime")) // 最大运行时间
	maxMem, _ := strconv.Atoi(c.PostForm("max_mem"))         // 最大内存

	// 获取数组参数
	categoryIds := c.PostFormArray("category_ids") // 分类ID列表
	testCases := c.PostFormArray("test_cases")     // 测试用例列表

	// 参数非空验证
	if identity == "" || title == "" || content == "" || len(categoryIds) == 0 ||
		len(testCases) == 0 || maxRuntime == 0 || maxMem == 0 {
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "参数不能为空", // 参数为空错误提示
		})
		return // 终止执行
	}

	// 使用数据库事务执行问题修改操作
	if err := models.DB.Transaction(func(tx *gorm.DB) error {
		// 1. 更新问题基础信息
		// 创建问题对象
		problem := &models.Problem{
			Identity:   identity,   // 唯一标识
			Title:      title,      // 问题标题
			Content:    content,    // 问题内容
			MaxRuntime: maxRuntime, // 最大运行时间
			MaxMem:     maxMem,     // 最大内存
		}
		// 根据identity更新数据库记录
		err := tx.Where("identity=?", identity).Updates(problem).Error
		if err != nil {
			return err
		}
		// 查询问题详情
		err = tx.Where("identity = ?", identity).Find(problem).Error
		if err != nil {
			return err
		}
		// 2. 更新关联的问题分类
		// 2.1. 删除旧的分类关联
		err = tx.Where("problem_id = ?", problem.ID).Delete(new(models.ProblemCategory)).Error
		if err != nil {
			return err // 删除失败，返回错误
		}

		// 2.2. 创建新的分类关联
		var pcs []*models.ProblemCategory // 创建空切片
		// 遍历分类ID列表，构建关联对象
		for _, id := range categoryIds {
			intId, _ := strconv.Atoi(id) // 字符串转整数
			pcs = append(pcs, &models.ProblemCategory{
				ProblemId:  problem.ID,  // 问题ID
				CategoryId: uint(intId), // 分类ID
			})
		}
		// 2.3 批量插入新的关联关系
		err = tx.Create(&pcs).Error
		if err != nil {
			return err // 创建失败，返回错误
		}
		// 3. 更新关联的测试用例
		// 3.1. 删除旧的测试用例关联
		err = tx.Where("problem_identity=?", identity).Delete(&models.TestCase{}).Error
		if err != nil {
			return err // 删除失败，返回错误
		}

		// 3.2. 创建新的测试用例关联
		var tcs []*models.TestCase // 创建测试用例切片

		// 遍历测试用例数据
		// {"input":"1 2\n","output":"3\n"}
		for _, testCase := range testCases {
			// JSON解析：将字符串转换为map
			caseMap := make(map[string]string)
			err := json.Unmarshal([]byte(testCase), &caseMap)
			if err != nil {
				return err // 解析失败
			}

			// 校验必填字段：检查输入是否存在
			if _, ok := caseMap["input"]; !ok {
				return errors.New("测试案例【input】格式错误")
			}
			if _, ok := caseMap["output"]; !ok {
				return errors.New("测试案例【output】格式错误")
			}

			// 构建测试用例对象并添加到切片
			tcs = append(tcs, &models.TestCase{
				Identity:        helper.GetUUID(),  // 生成唯一ID
				ProblemIdentity: identity,          // 关联问题ID
				Input:           caseMap["input"],  // 测试输入
				Output:          caseMap["output"], // 期望输出
			})
		}

		// 3.3 批量插入测试用例
		err = tx.Create(&tcs).Error
		if err != nil {
			return err // 插入失败
		}
		return nil
	}); err != nil { // 事务执行失败处理
		// 返回错误响应
		c.JSON(http.StatusOK, gin.H{
			"code": -1,
			"msg":  "Problem Modify Error:" + err.Error(), // 包含具体错误信息
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code": 200,
		"msg":  "问题修改成功",
	})
}
